using System;
using System.Text;
using System.Diagnostics;
using System.Reflection;

namespace AppExtensions.Helpers
{
  /// <summary>
  /// Helper class for constructing exception mesages.
  /// </summary>
  public static class ExceptionUtil
  {
    public static Exception NewException(string message)
    {
      return new Exception(FormatExceptionMessage(message));
    }

    public static Exception NewException(string message, Exception ex)
    {      
      return new Exception(FormatExceptionMessage(message, ex), ex);
    }

    public static Exception NewException(object source, string message)
    {
      return new Exception(FormatExceptionMessage(source, message));
    }

    public static Exception NewException(object source, string message, Exception ex)
    {
      return new Exception(FormatExceptionMessage(source, message, ex), ex);
    }

    /// <summary>
    /// Provides consistent formatting of the exception message to be thrown.
    /// </summary>
    /// <param name="message">message to be thrown</param>
    /// <returns>Formatted exception message.</returns>
    public static string FormatExceptionMessage(string message)
    {
        // Delegate processing to helper.
        return FormatExceptionMessage(GetExecutingClassName(), message);
    }
    /// <summary>
    /// Provides consistent formatting of the exception message to be thrown.
    /// </summary>
    /// <param name="message">message to be thrown</param>
    /// <param name="ex">actual cause of the exception.</param>
    /// <returns>Formatted exception message.</returns>
    public static string FormatExceptionMessage(string message, Exception ex)
    {
      // Delegate processing to helper.
      return FormatExceptionMessage(GetExecutingClassName(), message, ex);
    }
    /// <summary>
    /// Provides consistent formatting of the exception message to be thrown.
    /// </summary>
    /// <param name="source">object instance from where the formatting was requested.</param>
    /// <param name="message">message to be thrown</param>
    /// <returns>Formatted exception message.</returns>
    public static string FormatExceptionMessage(object source, string message)
    {
      // Delegate processing to helper.
      return FormatExceptionMessage(source.GetType(), message);
    }
    /// <summary>
    /// Provides consistent formatting of the exception message to be thrown.
    /// </summary>
    /// <param name="source">object instance from where the formatting was requested.</param>
    /// <param name="message">message to be thrown</param>
    /// <param name="ex">actual cause of the exception.</param>
    /// <returns>Formatted exception message.</returns>
    public static string FormatExceptionMessage(object source, string message, Exception ex)
    {
      // Delegate processing to helper.
      return FormatExceptionMessage(source.GetType(), message, ex);
    }
    /// <summary>
    /// Provides consistent formatting of the exception message to be thrown.
    /// </summary>
    /// <param name="type">object type from where the formatting was requested.</param>
    /// <param name="message">message to be thrown</param>
    /// <returns>Formatted exception message.</returns>
    public static string FormatExceptionMessage(Type type, string message)
    {
      // Delegate processing to helper.
      return FormatExceptionMessage(type.Name, message);
    }
    /// <summary>
    /// Provides consistent formatting of the exception message to be thrown.
    /// </summary>
    /// <param name="type">object type from where the formatting was requested.</param>
    /// <param name="message">message to be thrown</param>
    /// <param name="ex">actual cause of the exception.</param>
    /// <returns>Formatted exception message.</returns>
    public static string FormatExceptionMessage(Type type, string message, Exception ex)
    {
      // Delegate processing to helper.
      return FormatExceptionMessage(type.Name, message, ex);
    }
    /// <summary>
    /// Provides consistent formatting of the exception message to be thrown.
    /// </summary>
    /// <param name="className">name of the class where the exception occured.</param>
    /// <param name="message">message to be thrown</param>
    /// <returns>Formatted exception message.</returns>
    public static string FormatExceptionMessage(string className, string message)
    {
      // Call the overloaded method.
      return FormatExceptionMessage(className, message, null);
    }
    /// <summary>
    /// Provides consistent formatting of the exception message to be thrown.
    /// </summary>
    /// <param name="className">name of the class where the exception occured.</param>
    /// <param name="message">message to be thrown</param>
    /// <param name="ex">actual cause of the exception.</param>
    /// <returns>Formatted exception message.</returns>
    public static string FormatExceptionMessage(string className, string message, Exception ex)
    {
      StringBuilder sb = new StringBuilder();
      sb.AppendFormat("<br><br>{0}: ", className);
      sb.Append(message);
      if (null != ex)
      {
        sb.AppendFormat("; operation failed with error \"{0}\".<br><br>", ex.Message);
        sb.AppendFormat("<i>Base Exception Message</i>: \"{0}\"<br><br>", ex.GetBaseException().Message);
        sb.AppendFormat("<i>Base Exception Stack Trace</i>: {0}", ex.GetBaseException().StackTrace);
      }
      return sb.ToString();
    }

    private static string GetExecutingClassName()
    {
      string result = "Unknown";
      StackTrace trace = new StackTrace(false);

      for (int index = 0; index < trace.FrameCount; ++index)
      {
        StackFrame frame = trace.GetFrame(index);
        MethodBase method = frame.GetMethod();
        if (method.DeclaringType != typeof(ExceptionUtil))
        {
          result = method.DeclaringType.FullName;
          break;
        }
      }

      return result;
    }
  }
}